package com.googlecode.maurersmarbles;

import java.util.Map;
import java.util.TreeMap;

import android.graphics.Point;
import android.graphics.Rect;

/**
 * The class that is used to draw the board.
 */
public class Board {

	/**
	 * The scale factor used to calculate the size of the dealer chip.
	 */
	private static final float DEALER_CHIP_SCALE_FACTOR = 1.5f;
	/**
	 * The scale factor used to calculate the inner size of the board.
	 */
	private static final float INNER_SIZE_SCALE_FACTOR = 0.93f;
	/**
	 * The scale factor used to calculate the size of a marble on the board.
	 */
	private static final float MARBLE_SCALE_FACTOR = 0.75f;
	/**
	 * The scale factor used to calculate the size of a space on the board.
	 */
	private static final float SPACE_SCALE_FACTOR = 0.6f;
	/**
	 * The number of playable spaces on the board.
	 */
	public static final int BOARD_SPACES = 56;
	/**
	 * The size of the grid that makes up the spaces on the board.
	 */
	private static final int GRID_SIZE = 15;
	/**
	 * The indexes of the home entry space for each player.
	 */
	public static final int HOME_ENTRY[] = { 54, 12, 26, 40 };
	/**
	 * The max indexes of the home spaces for each player.
	 */
	public static final int HOME_MAX[] = { 59, 63, 67, 71 };
	/**
	 * The min indexes of the home spaces for each player.
	 */
	public static final int HOME_MIN[] = { 56, 60, 64, 68 };
	/**
	 * The indexes of the safe space for each player.
	 */
	public static final int SAFE[] = { 0, 14, 28, 42 };
	/**
	 * The max indexes of the start spaces for each player.
	 */
	public static final int START_MAX[] = { 75, 79, 83, 87 };
	/**
	 * The min indexes of the start spaces for each player.
	 */
	public static final int START_MIN[] = { 72, 76, 80, 84 };
	/**
	 * The map that is used to store the index to grid cell mapping.
	 */
	private static final Map<Integer, Point> INDEX_TO_GRID_CELL_MAP =
			new TreeMap<Integer, Point>();
	/**
	 * The size of each grid cell (width and height) on the board.
	 */
	private double gridCellSize;
	/**
	 * The size of a marble on the board.
	 */
	private int marbleSize;
	/**
	 * The size of a space on the board.
	 */
	private int spaceSize;
	/**
	 * The bounding box for the discard rectangle.
	 */
	private Rect discardRect = new Rect();
	/**
	 * The bounding box for the playable area on the board.
	 */
	private Rect innerRect = new Rect();
	/**
	 * The bounding box for the board.
	 */
	private Rect outerRect = new Rect();

	/**
	 * Creates a new Board.
	 */
	public Board() {
		// do nothing
	}

	/**
	 * Returns the bounding box for the given grid cell x/y and size.
	 * @param gridCellX the grid cell x
	 * @param gridCellY the grid cell y
	 * @param size the size of the object in the grid cell
	 * @return the bounding box for the given grid cell x/y and size
	 */
	public Rect getBoundingBox(double gridCellX, double gridCellY, double size) {
		Point startXY = getStartXYForGridCell(gridCellX, gridCellY, size);
		return new Rect(startXY.x, startXY.y, (int) (startXY.x + size),
				(int) (startXY.y + size));
	}

	/**
	 * Returns the bounding box for the given grid cell x/y.
	 * @param gridCellXY the grid cell x/y
	 * @return the bounding box for the given grid cell x/y
	 */
	public Rect getBoundingBox(Point gridCellXY) {
		return getBoundingBox(gridCellXY.x, gridCellXY.y, gridCellSize);
	}

	/**
	 * Returns the bounding box for the given grid cell x/y and size.
	 * @param gridCellXY the grid cell x/y
	 * @param size the size of the object in the grid cell
	 * @return the bounding box for the given grid cell x/y and size
	 */
	public Rect getBoundingBox(Point gridCellXY, double size) {
		return getBoundingBox(gridCellXY.x, gridCellXY.y, size);
	}

	/**
	 * Returns the bounding box for the dealer chip.
	 * @param player the player number that is the dealer
	 * @return the bounding box for the dealer chip
	 */
	public Rect getDealerChipRect(int player) {
		Point gridCell;
		switch (player) {
			default:
			case 0:
				gridCell = new Point(2, 12);
				break;
			case 1:
				gridCell = new Point(2, 2);
				break;
			case 2:
				gridCell = new Point(12, 2);
				break;
			case 3:
				gridCell = new Point(12, 12);
				break;
		}
		return getBoundingBox(gridCell, marbleSize * DEALER_CHIP_SCALE_FACTOR);
	}

	/**
	 * Returns the bounding box for the discard rectangle.
	 * @return the bounding box for the discard rectangle
	 */
	public Rect getDiscardRect() {
		return discardRect;
	}

	/**
	 * Returns the x/y value for each corner of the discard rectangle on the
	 * screen. The order of the corners is UL, UR, LR, LL.
	 * @return the x/y value for each corner of the discard rectangle on the
	 *         screen
	 */
	public Point[] getDiscardCornersOnScreen() {
		Point[] corners = new Point[4];
		corners[0] = new Point(discardRect.left, discardRect.top);
		corners[1] = new Point(discardRect.right, discardRect.top);
		corners[2] = new Point(discardRect.right, discardRect.bottom);
		corners[3] = new Point(discardRect.left, discardRect.bottom);
		return corners;
	}

	/**
	 * Returns the grid cell at the given x/y location.
	 * @param x the x location
	 * @param y the y location
	 * @return the grid cell at the given x/y location
	 */
	public Point getGridCellAtScreenXY(int x, int y) {
		if (innerRect.contains(x, y)) {
			int gridCellX = (int) ((x - innerRect.left) / gridCellSize);
			int gridCellY = (int) ((y - innerRect.top) / gridCellSize);
			return new Point(gridCellX, gridCellY);
		}
		return null;
	}

	/**
	 * Returns the cell for the given index.
	 * @param index the index to get the cell for
	 * @return the cell for the given index
	 */
	public Point getGridCellForIndex(int index) {
		return INDEX_TO_GRID_CELL_MAP.get(index);
	}

	/**
	 * Returns the index for the given cell.
	 * @param point the cell to get the index for
	 * @return the index for the given cell
	 */
	public int getIndexForGridCell(Point point) {
		for (Map.Entry<Integer, Point> entry : INDEX_TO_GRID_CELL_MAP
				.entrySet()) {
			if (entry.getValue().equals(point)) {
				return entry.getKey();
			}
		}
		return -1;
	}

	/**
	 * Returns the marble size.
	 * @return the marble size
	 */
	public int getMarbleSize() {
		return marbleSize;
	}

	/**
	 * Returns the bounding box of the board.
	 * @return the bounding box of the board
	 */
	public Rect getOuterRect() {
		return outerRect;
	}

	/**
	 * Returns the space size.
	 * @return the space size
	 */
	public int getSpaceSize() {
		return spaceSize;
	}

	/**
	 * Returns the start x/y location.
	 * @param gridCellX the cell number in the x dimension
	 * @param gridCellY the cell number in the y dimension
	 * @param size the size of the object in the grid cell
	 * @return the start x/y location
	 */
	protected Point getStartXYForGridCell(double gridCellX, double gridCellY,
			double size) {
		double offset = (gridCellSize - size) / 2.0;
		int x = (int) (innerRect.left + (gridCellX * gridCellSize) + offset);
		int y = (int) (innerRect.top + (gridCellY * gridCellSize) + offset);
		return new Point(x, y);
	}

	/**
	 * Returns the start x/y location.
	 * @param gridCellXY the point that contains the cell number in the x/y
	 *        dimensions
	 * @param size the size of the object in the grid cell
	 * @return the start x/y location
	 */
	public Point getStartXYForGridCell(Point gridCellXY, double size) {
		return getStartXYForGridCell((double) gridCellXY.x,
				(double) gridCellXY.y, size);
	}

	/**
	 * Sets the location and size of the board.
	 * @param startX the upper left x location
	 * @param startY the upper left y location
	 * @param size the width/height of the board
	 */
	public void setLocationAndSize(int startX, int startY, int size) {

		outerRect.set(startX, startY, startX + size, startY + size);

		// compute the inner board size and location
		int innerOffset =
				(int) (((1.0 - INNER_SIZE_SCALE_FACTOR) / 2.0) * size);
		int innerStartX = startX + innerOffset;
		int innerStartY = startY + innerOffset;
		int innerSize = (int) (size - (innerOffset * 2));
		innerRect.set(innerStartX, innerStartY, innerStartX + innerSize,
				innerStartY + innerSize);

		// save the size of each grid cell on the board
		gridCellSize = innerSize / (float) (GRID_SIZE);

		// save the size of the spaces and marbles on the board
		spaceSize = (int) (gridCellSize * SPACE_SCALE_FACTOR);
		marbleSize = (int) (gridCellSize * MARBLE_SCALE_FACTOR);

		// update the size and location of the discard rectangle
		int discardHeight = (int) (gridCellSize * 3);
		int discardWidth = (int) (discardHeight * Card.WIDTH_TO_HEIGHT_RATIO);
		int discardStartX =
				(int) ((innerSize / 2.0) + innerStartX - (discardWidth / 2.0));
		int discardStartY =
				(int) ((innerSize / 2.0) + innerStartY - (discardHeight / 2.0));
		discardRect.set(discardStartX, discardStartY, discardStartX
				+ discardWidth, discardStartY + discardHeight);

	}

	static {
		/**
		 * | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14
		 * ---|--------------------------------------------- 00 | 24 25 26 27 28
		 * 01 | 76 77 23 64 29 80 81 02 | 22 65 30 03 | 78 79 21 66 31 82 83 04
		 * | 20 67 32 05 | 14 15 16 17 18 19 33 34 35 36 37 38 06 | 13 39 07 |
		 * 12 60 61 62 63 71 70 69 68 40 08 | 11 41 09 | 10 09 08 07 06 05 47 46
		 * 45 44 43 42 10 | 04 59 48 11 | 72 73 03 58 49 84 85 12 | 02 57 50 13
		 * | 74 75 01 56 51 86 87 14 | 00 55 54 53 52
		 */
		// playable spaces
		INDEX_TO_GRID_CELL_MAP.put(0, new Point(5, 14));
		INDEX_TO_GRID_CELL_MAP.put(1, new Point(5, 13));
		INDEX_TO_GRID_CELL_MAP.put(2, new Point(5, 12));
		INDEX_TO_GRID_CELL_MAP.put(3, new Point(5, 11));
		INDEX_TO_GRID_CELL_MAP.put(4, new Point(5, 10));
		INDEX_TO_GRID_CELL_MAP.put(5, new Point(5, 9));
		INDEX_TO_GRID_CELL_MAP.put(6, new Point(4, 9));
		INDEX_TO_GRID_CELL_MAP.put(7, new Point(3, 9));
		INDEX_TO_GRID_CELL_MAP.put(8, new Point(2, 9));
		INDEX_TO_GRID_CELL_MAP.put(9, new Point(1, 9));
		INDEX_TO_GRID_CELL_MAP.put(10, new Point(0, 9));
		INDEX_TO_GRID_CELL_MAP.put(11, new Point(0, 8));
		INDEX_TO_GRID_CELL_MAP.put(12, new Point(0, 7));
		INDEX_TO_GRID_CELL_MAP.put(13, new Point(0, 6));
		INDEX_TO_GRID_CELL_MAP.put(14, new Point(0, 5));
		INDEX_TO_GRID_CELL_MAP.put(15, new Point(1, 5));
		INDEX_TO_GRID_CELL_MAP.put(16, new Point(2, 5));
		INDEX_TO_GRID_CELL_MAP.put(17, new Point(3, 5));
		INDEX_TO_GRID_CELL_MAP.put(18, new Point(4, 5));
		INDEX_TO_GRID_CELL_MAP.put(19, new Point(5, 5));
		INDEX_TO_GRID_CELL_MAP.put(20, new Point(5, 4));
		INDEX_TO_GRID_CELL_MAP.put(21, new Point(5, 3));
		INDEX_TO_GRID_CELL_MAP.put(22, new Point(5, 2));
		INDEX_TO_GRID_CELL_MAP.put(23, new Point(5, 1));
		INDEX_TO_GRID_CELL_MAP.put(24, new Point(5, 0));
		INDEX_TO_GRID_CELL_MAP.put(25, new Point(6, 0));
		INDEX_TO_GRID_CELL_MAP.put(26, new Point(7, 0));
		INDEX_TO_GRID_CELL_MAP.put(27, new Point(8, 0));
		INDEX_TO_GRID_CELL_MAP.put(28, new Point(9, 0));
		INDEX_TO_GRID_CELL_MAP.put(29, new Point(9, 1));
		INDEX_TO_GRID_CELL_MAP.put(30, new Point(9, 2));
		INDEX_TO_GRID_CELL_MAP.put(31, new Point(9, 3));
		INDEX_TO_GRID_CELL_MAP.put(32, new Point(9, 4));
		INDEX_TO_GRID_CELL_MAP.put(33, new Point(9, 5));
		INDEX_TO_GRID_CELL_MAP.put(34, new Point(10, 5));
		INDEX_TO_GRID_CELL_MAP.put(35, new Point(11, 5));
		INDEX_TO_GRID_CELL_MAP.put(36, new Point(12, 5));
		INDEX_TO_GRID_CELL_MAP.put(37, new Point(13, 5));
		INDEX_TO_GRID_CELL_MAP.put(38, new Point(14, 5));
		INDEX_TO_GRID_CELL_MAP.put(39, new Point(14, 6));
		INDEX_TO_GRID_CELL_MAP.put(40, new Point(14, 7));
		INDEX_TO_GRID_CELL_MAP.put(41, new Point(14, 8));
		INDEX_TO_GRID_CELL_MAP.put(42, new Point(14, 9));
		INDEX_TO_GRID_CELL_MAP.put(43, new Point(13, 9));
		INDEX_TO_GRID_CELL_MAP.put(44, new Point(12, 9));
		INDEX_TO_GRID_CELL_MAP.put(45, new Point(11, 9));
		INDEX_TO_GRID_CELL_MAP.put(46, new Point(10, 9));
		INDEX_TO_GRID_CELL_MAP.put(47, new Point(9, 9));
		INDEX_TO_GRID_CELL_MAP.put(48, new Point(9, 10));
		INDEX_TO_GRID_CELL_MAP.put(49, new Point(9, 11));
		INDEX_TO_GRID_CELL_MAP.put(50, new Point(9, 12));
		INDEX_TO_GRID_CELL_MAP.put(51, new Point(9, 13));
		INDEX_TO_GRID_CELL_MAP.put(52, new Point(9, 14));
		INDEX_TO_GRID_CELL_MAP.put(53, new Point(8, 14));
		INDEX_TO_GRID_CELL_MAP.put(54, new Point(7, 14));
		INDEX_TO_GRID_CELL_MAP.put(55, new Point(6, 14));
		// home spaces
		INDEX_TO_GRID_CELL_MAP.put(56, new Point(7, 13));
		INDEX_TO_GRID_CELL_MAP.put(57, new Point(7, 12));
		INDEX_TO_GRID_CELL_MAP.put(58, new Point(7, 11));
		INDEX_TO_GRID_CELL_MAP.put(59, new Point(7, 10));
		INDEX_TO_GRID_CELL_MAP.put(60, new Point(1, 7));
		INDEX_TO_GRID_CELL_MAP.put(61, new Point(2, 7));
		INDEX_TO_GRID_CELL_MAP.put(62, new Point(3, 7));
		INDEX_TO_GRID_CELL_MAP.put(63, new Point(4, 7));
		INDEX_TO_GRID_CELL_MAP.put(64, new Point(7, 1));
		INDEX_TO_GRID_CELL_MAP.put(65, new Point(7, 2));
		INDEX_TO_GRID_CELL_MAP.put(66, new Point(7, 3));
		INDEX_TO_GRID_CELL_MAP.put(67, new Point(7, 4));
		INDEX_TO_GRID_CELL_MAP.put(68, new Point(13, 7));
		INDEX_TO_GRID_CELL_MAP.put(69, new Point(12, 7));
		INDEX_TO_GRID_CELL_MAP.put(70, new Point(11, 7));
		INDEX_TO_GRID_CELL_MAP.put(71, new Point(10, 7));
		// start spaces
		INDEX_TO_GRID_CELL_MAP.put(72, new Point(1, 11));
		INDEX_TO_GRID_CELL_MAP.put(73, new Point(3, 11));
		INDEX_TO_GRID_CELL_MAP.put(74, new Point(1, 13));
		INDEX_TO_GRID_CELL_MAP.put(75, new Point(3, 13));
		INDEX_TO_GRID_CELL_MAP.put(76, new Point(1, 1));
		INDEX_TO_GRID_CELL_MAP.put(77, new Point(3, 1));
		INDEX_TO_GRID_CELL_MAP.put(78, new Point(1, 3));
		INDEX_TO_GRID_CELL_MAP.put(79, new Point(3, 3));
		INDEX_TO_GRID_CELL_MAP.put(80, new Point(11, 1));
		INDEX_TO_GRID_CELL_MAP.put(81, new Point(13, 1));
		INDEX_TO_GRID_CELL_MAP.put(82, new Point(11, 3));
		INDEX_TO_GRID_CELL_MAP.put(83, new Point(13, 3));
		INDEX_TO_GRID_CELL_MAP.put(84, new Point(11, 11));
		INDEX_TO_GRID_CELL_MAP.put(85, new Point(13, 11));
		INDEX_TO_GRID_CELL_MAP.put(86, new Point(11, 13));
		INDEX_TO_GRID_CELL_MAP.put(87, new Point(13, 13));

	}

}
